package com.demo.server.aspect;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson2.JSONObject;
import com.demo.server.annotation.HandleLog;
import com.demo.server.constant.CodeMsg;
import com.demo.server.constant.CommonConstant;
import com.demo.server.constant.HandleLogChoice;
import com.demo.server.entity.Log;
import com.demo.server.entity.User;
import com.demo.server.mapper.LogMapper;
import com.demo.server.util.AccessTokenUtil;
import com.demo.server.util.UUIDUtil;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

/**
 * @title 提取操作日志并写入数据库
 * @date 2024-02-22
 * @author wulianwei
 */
@ConditionalOnProperty(value = "custom.handleLog.enable")
@Aspect
@Component
public class HandleLogAspect {
	
	@Autowired
	private LogMapper logMapper;
	
	//@Pointcut("execution(* com.demo.server.controller.*.*(..))")
	@Pointcut("@annotation(com.demo.server.annotation.HandleLog)")
	public void handleLogPoint() {}
	
	
	@AfterThrowing(pointcut = "handleLogPoint()",throwing = "ex")
    public void handleLogAfterThrowing(Throwable ex) {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
		Log handleLog = (Log) request.getAttribute(CommonConstant.REQUEST_LOG_ATTR);
		if(handleLog == null) {
			return;
		}
		long time = (long) request.getAttribute(CommonConstant.REQUEST_TIME_ATTR);
		long wastetime = System.currentTimeMillis() - time;
		handleLog.setStatus("2"); //操作异常
		handleLog.setWaste(String.valueOf(wastetime));
		String exString = ex.toString();
		if(exString.length() > 500) {
			exString = exString.substring(0, 500);
		}
		handleLog.setResult(exString);
		logMapper.insert(handleLog);
        
    }
	
	@Around("handleLogPoint()")
    public Object handleLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        long time = System.currentTimeMillis();
        Log handleLog = packLog(request,joinPoint);
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        HandleLog logAnno = method.getAnnotation(HandleLog.class);
		if(logAnno.value() == HandleLogChoice.RECORD_ANY  || logAnno.value() == HandleLogChoice.RECORD_EXCEPTION) {
			request.setAttribute(CommonConstant.REQUEST_LOG_ATTR, handleLog);
	        request.setAttribute(CommonConstant.REQUEST_TIME_ATTR, time);
		}
        Object result = joinPoint.proceed();
        
        long wastetime = System.currentTimeMillis() - time;
        handleLog.setWaste(String.valueOf(wastetime));
        JSONObject resultJson = JSONObject.from(result);
        String resultStr = resultJson.toString();
        if(resultStr.length() > 500) {
        	resultStr = resultStr.substring(0,500);
		}
        handleLog.setResult(resultStr);
        if(logAnno.type() == HandleLogChoice.TYPE_LOGIN) {//登录操作, 重新记录用户信息
        	User user = AccessTokenUtil.getUser();
        	if(user != null) {
        		handleLog.setUserid(user.getId());
        		handleLog.setUsername(user.getUsername());
        	}
        }
        if(CodeMsg.SUCCESS.getCode().equals(resultJson.getInteger("code"))) {
        	handleLog.setStatus("0"); //操作成功
        	if(logAnno.value() == HandleLogChoice.RECORD_ANY  || logAnno.value() == HandleLogChoice.RECORD_SUC) {
        		 logMapper.insert(handleLog);
        	}
        }else {
        	handleLog.setStatus("1"); //操作失败
        	if(logAnno.value() == HandleLogChoice.RECORD_ANY  || logAnno.value() == HandleLogChoice.RECORD_FAIL) {
       		 	logMapper.insert(handleLog);
        	}
        }
        return result;
    }
	
	/**
	 * @title 封装日志
	 * @param request
	 * @param joinPoint
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Log packLog(HttpServletRequest request,JoinPoint joinPoint) {
		Log handleLog = new Log();
		handleLog.setId(UUIDUtil.get32UUID());
		handleLog.setCreatetime(new Date());
		handleLog.setIp(request.getRemoteAddr());
		handleLog.setStyle(request.getMethod());
		handleLog.setUrl(request.getRequestURI());
		Map<String,String[]> paramMap = request.getParameterMap();
    	if(paramMap != null&& paramMap.size() > 0) {
    		String paramStr =JSONObject.toJSONString(paramMap);
    		if(paramStr.length() > 200) {
    			paramStr = paramStr.substring(0,200);
    		}
    		handleLog.setParam(paramStr);
    	}
    	User user = AccessTokenUtil.getUser();
    	if(user != null) {
    		handleLog.setUserid(user.getId());
    		handleLog.setUsername(user.getUsername());
    	}
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		String methodName = signature.getDeclaringTypeName()+"."+signature.getName();
		handleLog.setMethod(methodName);
		Tag tag = (Tag) signature.getDeclaringType().getDeclaredAnnotation(Tag.class);
		 //设置模块
        if(tag != null) {
        	handleLog.setModule(tag.name());
        }
        
        Method method = signature.getMethod();
        Operation op = method.getDeclaredAnnotation(Operation.class);
        //设置功能
        if(op != null) {
        	handleLog.setOperation(op.summary());
        }
        Object[] args = joinPoint.getArgs();
        Parameter[] params = method.getParameters();
        for(int i=0;i<params.length;i++) {
        	Parameter param = params[i];
        	if(param.getAnnotation(RequestBody.class) != null) {
        		String paramStr = JSONObject.toJSONString(args[i]);
        		if(paramStr.length() > 200) {
        			paramStr = paramStr.substring(0,200);
        		}
        		handleLog.setBody(paramStr);
        	}
        }
        return handleLog;
	}
}
